package com.onlyxiahui.common.utils.base.net;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import com.onlyxiahui.common.utils.base.lang.bytes.BytesConvertUtil;
import com.onlyxiahui.common.utils.base.lang.primitive.PrimitiveConvertUtil;

/**
 * Date 2019-01-11 08:56:26<br>
 * Description 网上一个比较普遍的说法是InetAddress.getLocalHost().getHostAddress()<br>
 * 似乎很简单，但忽略了一个问题，即IP地址在现在的网络环境更加复杂了，比如有Lan，WIFI，蓝牙热点，虚拟机网卡...<br>
 * 即存在很多的网络接口（network interfaces），每个网络接口就包含一个IP地址，并不是所有的IP地址能被外部或局域网访问，<br>
 * 比如说虚拟机网卡地址等等。<br>
 * 也就是说InetAddress.getLocalHost().getHostAddress()的IP不一定是正确的IP。<br>
 * <p>
 * 写代码前，先明确一些规则：<br>
 * <p>
 * 1、127.xxx.xxx.xxx 属于"loopback" 地址，即只能你自己的本机可见，就是本机地址，比较常见的有127.0.0.1；<br>
 * 2、192.168.xxx.xxx 属于private 私有地址(site local address)，<br>
 * 属于本地组织内部访问，只能在本地局域网可见。<br>
 * 同样10.xxx.xxx.xxx、从172.16.xxx.xxx 到172.31.xxx.xxx都是私有地址，也是属于组织内部访问；<br>
 * 3、169.254.xxx.xxx 属于连接本地地址（link local IP），在单独网段可用<br>
 * 4、从224.xxx.xxx.xxx 到 239.xxx.xxx.xxx 属于组播地址<br>
 * 5、比较特殊的255.255.255.255 属于广播地址<br>
 * 6、除此之外的地址就是点对点的可用的公开IpV4地址<br>
 *
 * @author XiaHui<br>
 * @since 1.0.0
 */
public class AddressUtil {

	/**
	 * Description 根据域名获取ipV4 <br>
	 * Date 2019-04-01 08:55:27<br>
	 *
	 * @param url ：域名
	 * @return String
	 * @since 1.0.0
	 */
	public static String getIpV4ByName(String url) {
		String ip = null;
		try {
			InetAddress[] array = InetAddress.getAllByName(url);
			if (null != array) {
				for (InetAddress ia : array) {
					if (ia instanceof Inet4Address) {
						ip = ia.getHostAddress();
						break;
					}
				}
			}
		} catch (UnknownHostException ex) {
		}
		return ip;
	}

	/**
	 * Description 根据域名获取ipV6 <br>
	 * Date 2019-04-01 08:55:27<br>
	 *
	 * @param url ：域名
	 * @return String
	 * @since 1.0.0
	 */
	public static String getIpV6ByName(String url) {
		String ip = null;
		try {
			InetAddress[] array = InetAddress.getAllByName(url);
			if (null != array) {
				for (InetAddress ia : array) {
					if (ia instanceof Inet6Address) {
						if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress() && !ia.isLoopbackAddress()) {
							ip = ipV6FilterCardNo(ia);
							// ia.getHostAddress();
							break;
						}
					}
				}

				if (null == ip) {
					for (InetAddress ia : array) {
						if (ia instanceof Inet6Address) {
							if (!ia.isSiteLocalAddress()) {
								ip = ipV6FilterCardNo(ia);
								// ia.getHostAddress();
								break;
							}
						}
					}
				}

				if (null == ip) {
					for (InetAddress ia : array) {
						if (ia instanceof Inet6Address) {
							ip = ipV6FilterCardNo(ia);
							// ia.getHostAddress();
							break;
						}
					}
				}
			}
		} catch (UnknownHostException ex) {
		}
		return ip;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Description 获取本地IpV4，筛选逻辑为：先从物理网卡中筛选可用广域网地址->所有地址中找广域网地址->网卡中找本地地址<br>
	 * Date 2019-01-11 10:39:45<br>
	 *
	 * @return String
	 * @since 1.0.0
	 */
	public static String getLocalIpV4() {
		String ip = null;
		// 先获取物理网卡的地址，从物理网卡中筛选外网地址
		List<InetAddress> cardList = getCardInetAddressList();
		List<InetAddress> list = cardList;
		for (InetAddress ia : list) {
			if (ia != null && ia instanceof Inet4Address) {
				// 不能是127xxx、内网地址等
				if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress() && !ia.isLoopbackAddress()) {
					ip = ia.getHostAddress();
					break;
				}
			}
		}
		// 如果没有在物理网卡中找到、从所有地址选区外网地址
		if (null == ip) {
			list = getWanInetAddressList();
			for (InetAddress ia : list) {
				// IPV4
				if (ia != null && ia instanceof Inet4Address) {
					ip = ia.getHostAddress();
					break;
				}
			}
		}
		// 如果没有，继续从物理网卡筛选本地地址了
		if (null == ip) {
			list = cardList;
			for (InetAddress ia : list) {
				// IPV4
				if (ia != null && ia instanceof Inet4Address) {
					if (ia.isSiteLocalAddress()) {
						ip = ia.getHostAddress();
						break;
					}
				}
			}
		}
		if (null == ip) {
			list = getLanInetAddressList();
			for (InetAddress ia : list) {
				// IPV4
				if (ia != null && ia instanceof Inet4Address) {
					ip = ia.getHostAddress();
					break;
				}
			}
		}
		if (null == ip) {
			list = getInetAddressList();
			for (InetAddress ia : list) {
				// IPV4
				if (ia != null && ia instanceof Inet4Address) {
					ip = ia.getHostAddress();
					break;
				}
			}
		}
		return ip;
	}

	/**
	 * Description 获取本地IpV6 <br>
	 * Date 2019-04-01 08:56:40<br>
	 *
	 * @return String
	 * @since 1.0.0
	 */
	public static String getLocalIpV6() {
		String ip = null;
		List<InetAddress> cardList = getCardInetAddressList();
		List<InetAddress> list = cardList;
		for (InetAddress ia : list) {
			if (ia != null && ia instanceof Inet6Address) {
				if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress() && !ia.isLoopbackAddress()) {
					ip = ipV6FilterCardNo(ia);
					// ia.getHostAddress();
					break;
				}
			}
		}
		if (null == ip) {
			list = getWanInetAddressList();
			for (InetAddress ia : list) {
				// IPV6
				if (ia != null && ia instanceof Inet6Address) {
					ip = ipV6FilterCardNo(ia);
					// ia.getHostAddress();
					break;
				}
			}
		}

		if (null == ip) {
			list = cardList;
			for (InetAddress ia : list) {
				// IPV6
				if (ia != null && ia instanceof Inet6Address) {
					if (!ia.isLoopbackAddress()) {
						ip = ipV6FilterCardNo(ia);
						// ia.getHostAddress();
						break;
					}
				}
			}
		}

		if (null == ip) {
			list = getLanInetAddressList();
			for (InetAddress ia : list) {
				// IPV6
				if (ia != null && ia instanceof Inet6Address) {
					ip = ipV6FilterCardNo(ia);
					// ia.getHostAddress();
					break;
				}
			}
		}
		if (null == ip) {
			list = getInetAddressList();
			for (InetAddress ia : list) {
				// IPV6
				if (ia != null && ia instanceof Inet6Address) {
					ip = ipV6FilterCardNo(ia);
					// ia.getHostAddress();
					break;
				}
			}
		}
		return ip;
	}

	/**
	 * Description 获取本机IpV4列表 <br>
	 * Date 2019-04-01 08:56:57<br>
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getLocalIpV4List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV4
			if (ia != null && ia instanceof Inet4Address) {
				ip = ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}

	/**
	 * Description 获取本机IpV6列表 <br>
	 * Date 2019-04-01 08:57:08<br>
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getLocalIpV6List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV6
			if (ia != null && ia instanceof Inet6Address) {
				ip = ipV6FilterCardNo(ia);
				// ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Date 2019-01-11 10:38:36<br>
	 * Description 获取广域IpV4网列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getWanIpV4List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getWanInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV4
			if (ia != null && ia instanceof Inet4Address) {
				ip = ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}

	/**
	 * Date 2019-01-11 10:38:36<br>
	 * Description 获取广域IpV6网列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getWanIpV6List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getWanInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV6
			if (ia != null && ia instanceof Inet6Address) {
				ip = ipV6FilterCardNo(ia);
				// ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}
	/*-----------------------------------------------------------------------*/

	/**
	 * Date 2019-01-11 10:36:40<br>
	 * Description 获取局域网IpV4列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getLanIpV4List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getLanInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV4
			if (ia != null && ia instanceof Inet4Address) {
				ip = ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}

	/**
	 * Date 2019-01-11 10:35:49<br>
	 * Description 获取局域网IpV6列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getLanIpV6List() {
		List<String> ipList = new ArrayList<String>();

		List<InetAddress> list = getLanInetAddressList();
		String ip;
		for (InetAddress ia : list) {
			// IPV6
			if (ia != null && ia instanceof Inet6Address) {
				ip = ipV6FilterCardNo(ia);
				// ia.getHostAddress();
				ipList.add(ip);
			}
		}
		return ipList;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Date 2019-01-11 10:35:33<br>
	 * Description 获取广域网地址列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<InetAddress> getWanInetAddressList() {
		List<InetAddress> wanList = new ArrayList<>();
		List<InetAddress> list = getInetAddressList();
		for (InetAddress ia : list) {
			if (!ia.isSiteLocalAddress() && !ia.isLinkLocalAddress() && !ia.isLoopbackAddress()) {
				// IpV6 && ia.getHostAddress().indexOf(":") == -1
				wanList.add(ia);
			}
		}
		return wanList;
	}

	/**
	 * Date 2019-01-11 10:35:15<br>
	 * Description 获取局域网地址列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<InetAddress> getLanInetAddressList() {
		List<InetAddress> lanList = new ArrayList<>();
		List<InetAddress> list = getInetAddressList();
		for (InetAddress ia : list) {
			// 如果是site-local地址，就是它了
			if (ia.isSiteLocalAddress()) {
				lanList.add(ia);
			}
		}
		return lanList;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Description 获取本地Host名称 <br>
	 * Date 2019-04-01 09:00:25<br>
	 *
	 * @return String
	 * @since 1.0.0
	 */
	public static String getLocalHostName() {
		try {
			return InetAddress.getLocalHost().getHostName();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取Linux下的IP地址
	 *
	 */
//	private static String getLinuxLocalIP() {
//		String ip = "";
//		try {
//			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
//				NetworkInterface intf = en.nextElement();
//				String name = intf.getName();
//				if (!name.contains("docker") && !name.contains("lo")) {
//					for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
//						InetAddress inetAddress = enumIpAddr.nextElement();
//						if (!inetAddress.isLoopbackAddress()) {
//							String ipaddress = inetAddress.getHostAddress().toString();
//							if (!ipaddress.contains("::") && !ipaddress.contains("0:0:") && !ipaddress.contains("fe80")) {
//								ip = ipaddress;
//							}
//						}
//					}
//				}
//			}
//		} catch (SocketException ex) {
//			ip = "127.0.0.1";
//			ex.printStackTrace();
//		}
//		return ip;
//	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Date 2019-01-11 10:25:19<br>
	 * Description 按照"XX-XX-XX-XX-XX-XX"格式，获取本机MAC地址
	 *
	 * @return String
	 * @since 1.0.0
	 */
	public static String getMacAddress() {
		String mac = null;
		List<String> list = getCardMacAddressList();
		if (null != list && !list.isEmpty()) {
			mac = list.get(0);
		}
		if (mac == null) {
			list = getMacAddressList();
			if (null != list && !list.isEmpty()) {
				mac = list.get(0);
			}
		}
		return mac;
	}

	/**
	 * Date 2019-01-11 10:25:37<br>
	 * Description 按照"XX-XX-XX-XX-XX-XX"格式，获取本机MAC地址
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getMacAddressList() {
		List<String> macList = new ArrayList<String>();
		try {
			Enumeration<NetworkInterface> ni = NetworkInterface.getNetworkInterfaces();
			while (ni.hasMoreElements()) {
				NetworkInterface net = ni.nextElement();
				if (net != null) {
					byte[] bytes = net.getHardwareAddress();
					if (net.isUp() && bytes != null && bytes.length == 6) {
						StringBuilder sb = new StringBuilder();
						for (byte b : bytes) {
							// 与11110000作按位与运算以便读取当前字节高4位
							sb.append(Integer.toHexString((b & 240) >> 4));
							// 与00001111作按位与运算以便读取当前字节低4位
							sb.append(Integer.toHexString(b & 15));
							sb.append("-");
						}
						sb.deleteCharAt(sb.length() - 1);
						macList.add(sb.toString().toUpperCase());
					}
				}
			}
		} catch (Exception e) {
		}
		return macList;
	}

	/**
	 * 
	 * Description 获取所有网卡的地址 <br>
	 * Date 2019-04-01 09:08:22<br>
	 * 
	 * @return List
	 * @since 1.0.0
	 */
	public static List<String> getCardMacAddressList() {
		List<String> macList = new ArrayList<String>();
		try {
			Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
			NetworkInterface networkInterface;
			// 遍历所有的网络接口
			while (networkInterfaces.hasMoreElements()) {
				networkInterface = networkInterfaces.nextElement();

				if (networkInterface != null) {
					String name = networkInterface.getDisplayName();
					boolean isLoopback = networkInterface.isLoopback();
					// boolean isPointToPoint = networkInterface.isPointToPoint();
					boolean isUp = networkInterface.isUp();
					// boolean isVirtual = networkInterface.isVirtual();
					boolean isVm = isVirtual(name);
					if (!isLoopback && isUp && !isVm) {

						byte[] bytes = networkInterface.getHardwareAddress();
						if (bytes != null && bytes.length == 6) {
							StringBuilder sb = new StringBuilder();
							for (byte b : bytes) {
								// 与11110000作按位与运算以便读取当前字节高4位
								sb.append(Integer.toHexString((b & 240) >> 4));
								// 与00001111作按位与运算以便读取当前字节低4位
								sb.append(Integer.toHexString(b & 15));
								sb.append("-");
							}
							sb.deleteCharAt(sb.length() - 1);
							macList.add(sb.toString().toUpperCase());
						}
					}
				}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return macList;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Date 2019-01-11 10:31:47<br>
	 * Description 获取所有地址列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<InetAddress> getInetAddressList() {
		List<InetAddress> list = new ArrayList<InetAddress>();
		try {
			Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
			NetworkInterface networkInterface;
			Enumeration<InetAddress> inetAddresses;
			InetAddress inetAddress;
			// 遍历所有的网络接口
			while (networkInterfaces.hasMoreElements()) {
				networkInterface = networkInterfaces.nextElement();
				// 启用状态
				if (networkInterface != null && networkInterface.isUp()) {
					// 在所有的接口下再遍历IP
					inetAddresses = networkInterface.getInetAddresses();
					while (null != inetAddresses && inetAddresses.hasMoreElements()) {
						inetAddress = inetAddresses.nextElement();
						list.add(inetAddress);
					}
				}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * Date 2019-01-11 11:19:27<br>
	 * Description 获取物理机网卡列表
	 *
	 * @return List
	 * @since 1.0.0
	 */
	public static List<InetAddress> getCardInetAddressList() {
		List<InetAddress> list = new ArrayList<InetAddress>();
		try {
			Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
			NetworkInterface networkInterface;
			Enumeration<InetAddress> inetAddresses;
			InetAddress inetAddress;
			// 遍历所有的网络接口
			while (networkInterfaces.hasMoreElements()) {
				networkInterface = networkInterfaces.nextElement();

				if (networkInterface != null) {
					String name = networkInterface.getDisplayName();
					boolean isLoopback = networkInterface.isLoopback();
					// boolean isPointToPoint = networkInterface.isPointToPoint();
					boolean isUp = networkInterface.isUp();
					// boolean isVirtual = networkInterface.isVirtual();
					boolean isVm = isVirtual(name);
					if (!isLoopback && isUp && !isVm) {
						// 在所有的接口下再遍历IP
						inetAddresses = networkInterface.getInetAddresses();
						while (null != inetAddresses && inetAddresses.hasMoreElements()) {
							inetAddress = inetAddresses.nextElement();
							list.add(inetAddress);
						}
					}
				}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return list;
	}

	public static boolean isVirtual(String name) {
		boolean is = false;
		if (null != name) {
			List<String> list = new ArrayList<>();
			list.add("Virtual");
			list.add("VMware");
			for (String text : list) {
				String s = text.toLowerCase();
				String t = name.toLowerCase();
				if (t.indexOf(s) != -1) {
					is = true;
					break;
				}
			}
		}
		return is;
	}

	/**
	 * 
	 * Description 过滤网卡编号 <br>
	 * Date 2019-04-01 09:20:45<br>
	 * 
	 * @param ia
	 * @return String
	 * @since 1.0.0
	 */
	public static String ipV6FilterCardNo(InetAddress ia) {
		String address = ia.getHostAddress();
		if (null != address) {
			// Filter network card No
			int index = address.indexOf('%');
			if (index > 0) {
				address = address.substring(0, index);
			}
		}
		return address;
	}

	/*-----------------------------------------------------------------------*/

	/**
	 * Description ip string to int <br>
	 * Date 2019-04-01 08:59:42<br>
	 *
	 * @param ip
	 * @return int
	 * @since 1.0.0
	 */
	public static int ipV4ToInt(String ip) {
		try {
			InetAddress address = InetAddress.getByName(ip);
			byte[] bytes = address.getAddress();
			int a, b, c, d;
			a = BytesConvertUtil.byteToInt(bytes[0]);
			b = BytesConvertUtil.byteToInt(bytes[1]);
			c = BytesConvertUtil.byteToInt(bytes[2]);
			d = BytesConvertUtil.byteToInt(bytes[3]);
			int result = (a << 24) | (b << 16) | (c << 8) | d;
			return result;
		} catch (UnknownHostException e) {
			return 0;
		}
	}

	/**
	 * Description ip to long <br>
	 * Date 2019-04-01 08:59:15<br>
	 *
	 * @param ip
	 * @return long
	 * @since 1.0.0
	 */
	public static long ipV4ToLong(String ip) {
		int ipNum = ipV4ToInt(ip);
		return PrimitiveConvertUtil.intToLong(ipNum);
	}

	/**
	 * Description long to ip string <br>
	 * Date 2019-04-01 08:59:00<br>
	 *
	 * @param ip
	 * @return String
	 * @since 1.0.0
	 */
	public static String longToIpV4(long ip) {
		int[] b = new int[4];
		b[0] = (int) ((ip >> 24) & 0xff);
		b[1] = (int) ((ip >> 16) & 0xff);
		b[2] = (int) ((ip >> 8) & 0xff);
		b[3] = (int) (ip & 0xff);
		String x;
		x = Integer.toString(b[0]) + "." + Integer.toString(b[1]) + "." + Integer.toString(b[2]) + "." + Integer.toString(b[3]);
		return x;
	}
}
